package com.faf.service.impl;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import com.faf.bean.Likes;
import com.faf.bean.User;
import com.faf.dao.LikesDao;
import com.faf.dao.UserDao;
import com.faf.dao.impl.LikesDaoImpl;
import com.faf.dao.impl.UserDaoImpl;
import com.faf.service.ItemSimilarity;

public class ItemSimilarityImpl implements ItemSimilarity
{

	@Override
	public ArrayList<Integer> recommend(int userid) throws SQLException
	{
		//定义每个容器的大小Size
		int Size = 0;

		UserDao userDao = new UserDaoImpl();//用户数据访问层
		LikesDao likesDao = new LikesDaoImpl();//喜欢表数据访问层
		List<User> userList = userDao.getUserList();//当前登录用户的喜欢列表
		List<Likes> allLikesList = likesDao.getLikesList();//所有用户喜欢菜品
		Set<Integer> likeFoods = new TreeSet<>();//treeset容器可以实现以排序二叉树的形式存储数据，该容器可以除重
		//将所有用户喜欢的食物以从小到大形式存入likeFoods数组。
		for (Likes likes : allLikesList)
		{
			//所有用户喜欢的菜品清单
			likeFoods.add(likes.getFoodId());
		}
		Size = likeFoods.size();//得到所有用户喜欢食物的总数
		//用户所喜欢的菜品
		List<Likes> userLikesList = likesDao.getLikesListById(userid);
		//所有人喜欢的全部菜品的id，以及其对应的喜欢人数
		int[][] allLikeFoodId = new int[2][Size];
		//Iterator可以实现遍历likeFoods数组。
		Iterator<Integer> it = likeFoods.iterator();
		int temp = 0;
		while(it.hasNext()) {
			allLikeFoodId[0][temp] = it.next();
			temp++;
		}
		
		int[][] co_OccurrenceMatrix = new int[Size][Size]; //共现矩阵 
		//初始化共现矩阵
		for (int i = 0; i < co_OccurrenceMatrix.length; i++)
		{
			for (int j = 0; j < co_OccurrenceMatrix.length; j++)
			{
				co_OccurrenceMatrix[i][j] = 0;
			}
		}
		//1、得到该用户的共现矩阵
		for (User user : userList)
		{
			if(user.getUserId() == userid) continue;
			//获得当前用户所喜欢的食物列表
			List<Likes> foodListOfOtherUserLike = new ArrayList<Likes>();
			for (Likes likes : allLikesList)
			{
				if(likes.getUserId() == user.getUserId()) {
					foodListOfOtherUserLike.add(likes);
				}
			}
			//a数组用来存储当前用所喜欢菜的在allLikeFoodId[0]中的那一列
			int[] a = new int[foodListOfOtherUserLike.size()];
			for (int i = 0; i < foodListOfOtherUserLike.size(); i++)
			{
				for (int j = 0; j < Size; j++)
				{
					if(allLikeFoodId[0][j] == foodListOfOtherUserLike.get(i).getFoodId()) {
						a[i] = j;
						allLikeFoodId[1][j]++;
						break;
					}
				}
			}
			
			for (int i = 0; i < a.length; i++)
			{
				for (int j = i+1; j < a.length; j++)
				{
					co_OccurrenceMatrix[a[i]][a[j]]++;
					co_OccurrenceMatrix[a[j]][a[i]]++;
				}
			}
			
		}
		//2、求出用户喜欢的菜与其他菜的相似度W（其中不包括用户喜欢的菜）
		int n1 = userLikesList.size();//用户所喜欢的菜的个数
		int n2 = Size - n1;  //除用户喜欢的菜外其他菜的总数

		int[] a1 = new int[n1];//存储用户喜欢菜的下标
		double[] a2 = new double[n2];//存储其他用户喜欢菜的下标
		//重置n1、n2
		n1 = 0;//a1数组的下标
		n2 = 0;//a2[0]数组的下标
		//将用户喜欢菜的下标找出并存入a1数组
		for (int i = 0; i < userLikesList.size(); i++)
		{
			for (int j = 0; j < Size; j++)
			{
				if(userLikesList.get(i).getFoodId() == allLikeFoodId[0][j]) {
					a1[n1] = j;
					n1++;
				}
			}
		}
		//将除用户喜欢菜外的下标找出并存入a2数组
		for (int i = 0; i < Size; i++)
		{
			int flag1 = 0;
			for (int j = 0; j < n1; j++)
			{
				if(a1[j] != i) flag1++;
			}
			if(flag1 == n1) {
				a2[n2] = i;
				n2++;
			}
		}
		//recommendfoods用来存储最喜欢和次喜欢的推荐菜的序号和其相似度
		double[][] recommendfoods = new double[2][2];
		//初始化recommendfoods
		for (int i = 0; i < recommendfoods.length; i++)
		{
			for (int j = 0; j < recommendfoods.length; j++)
			{
				recommendfoods[i][j] = 0;
			}
		}
		//通过相似度计算得出找出推荐菜品序号
		for (int i = 0; i < a2.length; i++)
		{
			for (int j = 0; j < a1.length; j++)
			{

				int temp2 = (int)a2[i];
				double temp3 = co_OccurrenceMatrix[a1[j]][temp2] / Math.sqrt(allLikeFoodId[1][a1[j]]*allLikeFoodId[1][temp2]);
				if(recommendfoods[0][0] != a2[i] && recommendfoods[1][0] < temp3) {
					//发现新的最大相似度的菜，将旧的最大相似度替换为第二大相似度的菜
					recommendfoods[0][1] = recommendfoods[0][0];
					recommendfoods[1][1] = recommendfoods[1][0];
					recommendfoods[0][0] = a2[i];
					recommendfoods[1][0] = temp3;
					
				}else if(recommendfoods[0][0] != a2[i] && recommendfoods[1][1] < temp3) {
					//发现比第一个推荐菜相似度小但比第二个菜相似度高的菜
					recommendfoods[0][1] = a2[i];
					recommendfoods[1][1] = temp3;
				}
			}
		}
		
		ArrayList<Integer> recommendfoodsarr = new ArrayList<Integer>();//存储推荐菜品的id
		//通过推荐菜品的序号可以找出推荐菜品的id
		if(recommendfoods[1][0] != 0.0 && recommendfoods[1][1] != 0.0) {
			for (int i = 0; i < 2; i++)
			{
				recommendfoodsarr.add(allLikeFoodId[0][(int)recommendfoods[0][i]]);
			}
		}else if(recommendfoods[1][0] != 0.0){
			//如果遇到冷启动，直接给用户推荐TopN
			//recommendfoodsarr.add((int)recommendfoods[1][0]);
		}
		return recommendfoodsarr;
	}
	

}
